home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1999 July
/
Macworld (1999-07).dmg
/
Shareware World
/
Info
/
For Developers
/
Mops 3.4.sea
/
Mops source
/
System source
/
floating point
< prev
next >
Wrap
Text File
|
1995-02-06
|
38KB
|
1,829 lines
\ Aug 90 mrh Mops version.
\ Dec 90 mrh Direct FPU support added - float now 14 bytes long.
\ Apr 91 mrh More optimization of FPU code. FP source files combined.
\ Number of parms/locals increased via ExtraLocals area.
\ The floating heap is a region of heap that is divided into 14-byte blocks.
\ Each block consists of two bytes of status information, along with 12
\ bytes of data. If the status field is $0001, the block is in use.
\ Otherwise, the status field holds the offset of the next free block from
\ the start of the array, and bit 0 is off because the offset must be even.
\ The data field is a 96-bit floating point number in 68881/68882 FPU
\ extended format. This is basically the same as the SANE 80-bit
\ extended format, since the 3rd and 4th bytes are unused (zero), and
\ the SANE format is identical except that these unused 2 bytes are not
\ represented. If we don't have an FPU we call SANE, and this means we
\ have to adjust the format first.
false -> useFPU?
\ floating-point error handlers
: NOTINIT cr ." Uninitialized float argument" abort ;
: FullErr cr ." Floating point heap is full" abort ;
: NF cr ." Not a float: " . abort ;
:code NoFloat \ Assume A0 -> float that isn't
push.l a0
bra.s dic[NF]
;code
:class FLTHEAP super{ object } 14 indexed
record
{ int FreeHead \ offset of first free block
}
:mcode NEW: \ ( -- fPtr )
\ Returns a ptr to a new block. Interestingly,
\ the Mops register usage means that this routine is only
\ half as long as it was in Neon. Note that unlike Neon,
\ fPtr points to the floating data, not to the status word.
loc
move.w (a2),d0 ; D0(lo) = offset of first free block
beq dic[fullErr]
lea 0(a2,d0.w),a0 ; A0 -> the block
move.w (a0),(a2) ; Move next free block
; offs to free list hdr
move.w #1,(a0)+ ; Mark block in use
push.l a0 ; Return data addr
;mcode
:mcode RELEASE: \ ( fptr -- ) Disposes of block for fptr
pop.l a0 ; A0 -> float data
cmpi.w #1,-(a0) ; Float block must have $0001 in
; its status field
bne dic[noFloat]
move.w (a2),(a0) ; Move free list hdr to blk
; being freed
sub.l a2,a0 ; Get offs of block
move.w a0,(a2) ; Store in free head ptr
;mcode
:m ROOM: { \ offs #free -- #free } \ Returns # of float blocks remaining
\ in float heap
get: freeHead -> offs 0 -> #free
BEGIN offs
WHILE offs ^base + w@ -> offs 1 ++> #free
REPEAT
#free ;m
:m CLASSINIT: \ Sets all blocks to free and links them together.
limit 1- 0
DO
i 1+ ^elem ^base - i ^elem w!
LOOP
0 limit 1- ^elem w!
0 ^elem ^base - put: freeHead ;m
:m INIT: classinit: self ;m
;class
100 fltHeap FLTMEM
\ (FLTNEW) is a subroutine which returns a new float ptr in A0.
\ Uses D0.
:code (FLTNEW)
loc
lea dicobj[fltMem],a0
move.w (a0),d0 ; D0(lo) = offset of first free
; block
beq dic[fullErr]
add.w d0,a0 ; A0 -> the block
move.w (a0),dicobj[fltMem] ; Move next free block offs to
; free list hdr
move.w #1,(a0)+ ; Mark block in use, update A0 to
; float data addr
;code
\ (FLTDISP) is a subroutine to dispose of the float in A0. Uses A0.
:code (FLTDISP)
push.l a1 ; Save a1
cmpi.w #1,-(a0) ; Float block must have $0001 in
; its status field
bne dic[noFloat]
lea dicobj[fltMem],a1
move.w (a1),(a0) ; Move free list hdr to blk being
; freed
sub.l a1,a0 ; Get offset of block
move.w a0,(a1) ; Store in free list header
pop.l a1 ; Restore a1
;code
:code (FLTDISP2) \ Subroutine to dispose of floats in A0,A1
\ Uses A0, A1, D0, D1
move.l a1,d1 ; Save
cmpi.w #1,-(a0) ; Float must have $0001 in its status field
bne dic[noFloat]
lea dicobj[fltMem],a1
move.w (a1),(a0) ; Move free list hdr to blk being freed
sub.l a1,a0 ; Get offset of block
move.w a0,d0 ; Save in D0
move.l d1,a0 ; Now the other one.
cmpi.w #1,-(a0) ; Float must have $0001 in its
; status field
bne dic[noFloat]
move.w d0,(a0) ; Move next free blk offs to blk being freed
sub.l a1,a0 ; Get offset of block
move.w a0,(a1) ; Store in free list header
;code
:code FLIT
bsr dic[(fltNew)] ; New float ptr to A0
push.l a0 ; Push it
move.l (a7),a1
move.w (a1)+,(a0)+ ; Literal is in 80-bit format
clr.w (a0)+ ; Expand to FPU format
move.l (a1)+,(a0)+
move.l (a1)+,(a0)
move.l a1,(a7) ; Update return address
;code
:code (FPULIT)
move.l (a7)+,a1
jmp 12(a1)
;code
:code FDUP
bsr dic[(fltNew)] ; New float to A0
move.l (a6),a1 ; Float to dup to A1
push.l a0 ; Push new float
move.w -2(a1),-2(a0) ; Move status word
movem.l (a1),d0-d2 ; Move data
movem.l d0-d2,(a0)
;code
:code FOVER
bsr dic[(fltNew)] ; New float to A0
move.l 4(a6),a1 ; Float to copy to A1
push.l a0 ; Push new float
move.w -2(a1),-2(a0) ; Move status word
movem.l (a1),d0-d2 ; Move data
movem.l d0-d2,(a0)
;code
: F2DUP fOver fOver ;
:code FDROP
pop.l a0
bra dic[(fltDisp)]
;code
:code F2DROP
pop.l a0
pop.l a1
bra dic[(fltDisp2)]
;code
( ops opCode -- )
: FP68K \ Call FP68K. Floating-point package.
makeint call pack4 ;
: ELEMS68K \ Call ELEMS68K. Transcendentals package.
makeint call pack5 ;
\ ==============================
\ FP code words
\ ==============================
$ 4E58 constant XINFOMK \ Must agree with defn in Defn.asm *****
: :FP1 \ ( opcode -- )
header
-80 w, \ handler code FP1_h
xinfoMk w, \ Marks this word as having extra non-code info
2 w, \ which is 2 bytes long
w, \ This is it -- the opcode
postpone ] \ start compiling
; immediate
: :FP2 \ ( opcode -- )
header
-82 w, \ handler code FP2_h
xinfoMk w, \ Marks this word as having extra non-code info
2 w, \ which is 2 bytes long
w, \ This is it -- the opcode
postpone ] \ start compiling
; immediate
: :FPcmp \ ( opcode -- )
header
-84 w, \ handler code FPcmp_h
xinfoMk w, \ Marks this word as having extra non-code info
2 w, \ which is 2 bytes long
w, \ This is it -- the opcode
postpone ] \ start compiling
; immediate
\ =========== Dyadic comparisons ==========
:code FCMP2 \ ( flt0 flt1 -- abs1 abs2) Subroutine to set up stack for
\ dyadic comparison and kill floats.
\ Uses D0,D1,D2 and A0,A1.
loc
fcmp2 pop.l a1 ; A1 -> flt1
move.l (a6),a0 ; A0 -> flt0
move.w (a0)+,(a0) ; Convert both to 80-bit SANE format
move.w (a1)+,(a1)
move.l a1,(a6)
push.l a0 ; Push addrs for SANE call. Note
; SANE operands are reversed.
subq #2,a0 ; Restore original float to A0/1
; for (fltDisp2)
subq #2,a1
moveq #0,d2 ; Ready for result
bra dic[(fltDisp2)] ; Kill floats (but data still valid)
;code
:code FPUCMP2 \ ( flt0 flt1 -- ) Subroutine to set up FPU for comparison.
FPUcmp2 pop.l a1
pop.l a0
fmove.x (a0),fp0
;code
\ Stack frame for all dyadic comparisons:
\ ( float1 float2 -- b )
\ If we have an FPU, we use it. In this case we defer as much
\ housekeeping as possible to the time after the floating comparison
\ but before we test the FPU condition code. This time comes almost
\ free of charge since it will be overlapped with the comparison op.
$ 3F0E :FPcmp F>
ToCode
loc
tst.b 3(dic[FPU?])
beq.s noFPU1
bsr.s dic[FPUcmp2]
fcmp.x (a1),fp0
bsr dic[(fltDisp2)]
fsgt d2
FixBool ext.w d2
ext.l d2
push.l d2
rts
noFPU1 bsr.s dic[Fcmp2] ; Setup
push.w #8 ; Code for FCMPX
exg a6,a7
call pack4
exg a6,a7
sgt d2
bra.s fixBool
;code
$ 3F0D :FPcmp F<
ToCode
tst.b 3(dic[FPU?])
beq.s noFPU2
bsr dic[FPUcmp2]
fcmp.x (a1),fp0
bsr dic[(fltDisp2)]
fslt d2
bra fixBool
noFPU2 bsr dic[Fcmp2] ; Setup
push.w #8 ; Code for FCMPX
exg a6,a7
call pack4
exg a6,a7
slt d2
bra fixBool
;code
$ 3F0C :FPcmp F>=
ToCode
tst.b 3(dic[FPU?])
beq.s noFPU3
bsr dic[FPUcmp2]
fcmp.x (a1),fp0
bsr dic[(fltDisp2)]
fsge d2
bra fixBool
noFPU3 bsr dic[Fcmp2] ; Setup
push.w #8 ; Code for FCMPX
exg a6,a7
call pack4
exg a6,a7
sge d2
bra fixBool
;code
$ 3F0F :FPcmp F<=
ToCode
tst.b 3(dic[FPU?])
beq.s noFPU4
bsr dic[FPUcmp2]
fcmp.x (a1),fp0
bsr dic[(fltDisp2)]
fsle d2
bra fixBool
noFPU4 bsr dic[Fcmp2] ; Setup
push.w #8 ; Code for FCMPX
exg a6,a7
call pack4
exg a6,a7
sle d2
bra fixBool
;code
$ 3F07 :FPcmp F=
ToCode
tst.b 3(dic[FPU?])
beq.s noFPU5
bsr dic[FPUcmp2]
fcmp.x (a1),fp0
bsr dic[(fltDisp2)]
fseq d2
bra fixBool
noFPU5 bsr dic[Fcmp2] ; Setup
push.w #8 ; Code for FCMPX
exg a6,a7
call pack4
exg a6,a7
seq d2
bra fixBool
;code
$ 3F06 :FPcmp F<>
ToCode
tst.b 3(dic[FPU?])
beq.s noFPU6
bsr dic[FPUcmp2]
fcmp.x (a1),fp0
bsr dic[(fltDisp2)]
fsne d2
bra fixBool
rts
noFPU6 bsr dic[Fcmp2] ; Setup
push.w #8 ; Code for FCMPX
exg a6,a7
call pack4
exg a6,a7
sne d2
bra fixBool
;code
\ ========= Monadic comparisons ==========
variable FZERO 0 , 0 , \ Source of zero
:code FCMP1 \ ( flt -- abs ) Subroutine to set up stack for
\ monadic comparison and kill float.
\ Uses D0,D1,D2 and A0,A1.
loc
fcmp1 move.l (a6),a0 ; A0 -> flt
move.w (a0)+,(a0) ; Convert to 80-bit SANE format
lea dic[FZero],a1
move.l a1,(a6)
push.l a0
subq #2,a0 ; Restore original float to A0 for
; (fltDisp)
moveq #0,d2 ; Ready for result
bra dic[(fltDisp)] ; Kill float (but data still
; valid)
;code
$ 3F17 :FPcmp F0=
ToCode
loc
tst.b 3(dic[FPU?])
beq.s noFPU1
pop.l a0
ftst.x (a0),fp0
bsr dic[(fltDisp)]
fseq d2
FixBool ext.w d2
ext.l d2
push.l d2
rts
noFPU1 bsr dic[Fcmp1] ; Setup
push.w #8 ; Code for FCMPX
exg a6,a7
call pack4
exg a6,a7
seq d2
bra.s fixBool
;code
$ 3F16 :FPcmp F0<>
ToCode
tst.b 3(dic[FPU?])
beq.s noFPU2
pop.l a0
ftst.x (a0),fp0
bsr dic[(fltDisp)]
fsne d2
bra fixBool
noFPU2 bsr dic[Fcmp1] ; Setup
push.w #8 ; Code for FCMPX
exg a6,a7
call pack4
exg a6,a7
sne d2
bra fixBool
;code
$ 3F1C :FPcmp F0>=
ToCode
tst.b 3(dic[FPU?])
beq.s noFPU3
pop.l a0
ftst.x (a0),fp0
bsr dic[(fltDisp)]
fsge d2
bra fixBool
noFPU3 bsr dic[Fcmp1] ; Setup
push.w #8 ; Code for FCMPX
exg a6,a7
call pack4
exg a6,a7
sge d2
bra fixBool
;code
$ 3F1D :FPcmp F0<
ToCode
tst.b 3(dic[FPU?])
beq.s noFPU4
pop.l a0
ftst.x (a0),fp0
bsr dic[(fltDisp)]
fslt d2
bra fixBool
noFPU4 bsr dic[Fcmp1] ; Setup
push.w #8 ; Code for FCMPX
exg a6,a7
call pack4
exg a6,a7
slt d2
bra fixBool
;code
$ 3F1F :FPcmp F0<=
ToCode
tst.b 3(dic[FPU?])
beq.s noFPU5
pop.l a0
ftst.x (a0),fp0
bsr dic[(fltDisp)]
fsle d2
bra fixBool
noFPU5 bsr dic[Fcmp1] ; Setup
push.w #8 ; Code for FCMPX
exg a6,a7
call pack4
exg a6,a7
sle d2
bra fixBool
;code
$ 3F1E :FPcmp F0>
ToCode
tst.b 3(dic[FPU?])
beq.s noFPU6
pop.l a0
ftst.x (a0),fp0
bsr dic[(fltDisp)]
fsgt d2
bra fixBool
noFPU6 bsr dic[Fcmp1] ; Setup
push.w #8 ; Code for FCMPX
exg a6,a7
call pack4
exg a6,a7
sgt d2
bra fixBool
;code
\ =============== Arithmetic operators ==============
:code FOP2 \ ( flt0 flt1 -- flt0 addr1 addr0 ) Subroutine to set up for
\ 2-operand operation, where flt0 takes the result.
loc
pop.l a0 ; A0 -> flt1
move.l (a6),a1 ; A1 -> flt0. Also leave on stk for result.
move.w (a0)+,(a0) ; Convert both to 80-bit SANE format
move.w (a1)+,(a1)
push.l a0 ; Push addrs for SANE call. Note SANE
push.l a1 ; operands are reversed.
subq #2,a0 ; Restore original flt1 addr to A0 for (fltDisp)
bra dic[(fltDisp)] ; Kill flt1 (but data still valid)
;code
:code FOP1
move.l (a6),a0
move.w (a0)+,(a0)
push.l a0
;code
:code ADJUST_RESULT \ ( flt -- flt )
move.l (a6),a0
move.w 2(a0),(a0)
clr.w 2(a0)
;code
\ ( f1 f2 -- f1<op>f2 ) Result gets stored in f1's data.
$ 41 :fp2 F+
ToCode
loc
tst.b 3(dic[FPU?])
beq.s noFPU
pop.l a0
move.l (a6),a1
fmove.x (a1),fp0
fadd.x (a0),fp0
bsr dic[(fltDisp)]
move.l (a6),a1
fmove.x fp0,(a1)
rts
noFPU bsr.s dic[fop2] ; Setup
clr.w -(a6) ; Code for FADDX
exg a6,a7
call pack4
exg a6,a7
bra dic[adjust_result]
;code
$ 48 :fp2 F-
ToCode
loc
tst.b 3(dic[FPU?])
beq.s noFPU
pop.l a0
move.l (a6),a1
fmove.x (a1),fp0
fsub.x (a0),fp0
bsr dic[(fltDisp)]
move.l (a6),a1
fmove.x fp0,(a1)
rts
noFPU bsr dic[fop2] ; Setup
push.w #2 ; Code for FSUBX
exg a6,a7
call pack4
exg a6,a7
bra dic[adjust_result]
;code
$ 42 :fp2 F*
ToCode
loc
tst.b 3(dic[FPU?])
beq.s noFPU
pop.l a0
move.l (a6),a1
fmove.x (a1),fp0
fmul.x (a0),fp0
bsr dic[(fltDisp)]
move.l (a6),a1
fmove.x fp0,(a1)
rts
noFPU bsr dic[fop2] ; Setup
push.w #4 ; Code for FMULX
exg a6,a7
call pack4
exg a6,a7
bra dic[adjust_result]
;code
$ 49 :fp2 F/
ToCode
loc
tst.b 3(dic[FPU?])
beq.s noFPU
pop.l a0
move.l (a6),a1
fmove.x (a1),fp0
fdiv.x (a0),fp0
bsr dic[(fltDisp)]
move.l (a6),a1
fmove.x fp0,(a1)
rts
noFPU bsr dic[fop2] ; Setup
push.w #6 ; Code for FDIVX
exg a6,a7
call pack4
exg a6,a7
bra dic[adjust_result]
;code
\ ============= Monadic operations ==============
\ FNEGATE and FABS simply operate on the sign bit, so we don't need to
\ call SANE at all. The SANE manual actually recommends this.
$ 55 :fp1 FNEGATE
toCode
move.l (a6),a0
bchg #7,(a0)
;code
$ 54 :fp1 FABS
toCode
move.l (a6),a0
bclr #7,(a0)
;code
$ 5A :fp1 SQRT
ToCode
loc
tst.b 3(dic[FPU?])
beq.s noFPU
move.l (a6),a0
fsqrt.x (a0),fp0
fmove.x fp0,(a0)
rts
noFPU move.l (a6),a0
move.w (a0)+,(a0)
push.l a0
exg a6,a7
move.w #$12,-(a7) ; FSQRTX
call pack4
exg a6,a7
bra dic[adjust_result]
;code
hex
: ROUND fop1 w 14 call pack4 adjust_result ;
: TRUNC fop1 w 16 call pack4 adjust_result ;
: LOGBIN fop1 w 1A call pack4 adjust_result ;
decimal
:code SCALEBIN \ ( x n -- x*(2**n) )
loc
pop.l hi
move.l (a6),a0
exg a6,a7
pea lo
move.w (a0)+,(a0)
move.l a0,-(a7)
move.w #$18,-(a7) ; FSCALBX
call pack4
exg a6,a7
bra dic[adjust_result]
hi dc.w 0
lo dc.w 0
;code
\ =========== Conversion to/from integers ============
:code FLOAT> \ ( flt -- int32 )
\ Special note: the 68040's integrated FP doesn't implement
\ FINTRZ -- so it's handled via a trap. We definitely need to
\ avoid this instruction!!! The conversion can simply be done
\ by FMOVEint the float to a D register.
move.l (a6),d2 ; Source float
move.l d2,a0
bsr dic[(fltDisp)] ; Kill it
move.l d2,a0
move.b 3(dic[useFPU?]),d0
beq.s noFPU
fmove.x (a0),fp0 ; get the number
fmove.l fp0,(a6) ; convert to integer
rts
noFPU move.w (a0)+,(a0)
move.l a6,d0 ; Save result cell addr
push.l a0 ; Source (the float data)
push.l d0 ; Dest (the result cell)
push.w #$2810 ; Extended to Longint
exg a6,a7
call pack4
exg a6,a7
;code
:code FLOAT>D \ ( flt -- int64 ) We've added this in case someone
\ needs to convert to a double integer. SANE Comp
\ format is essentially a double integer (the only
\ difference is the special NaN value
\ $8000 0000 0000 0000)
move.l (a6),d2 ; Source float
move.l d2,a0
bsr dic[(fltDisp)] ; Kill it
move.l d2,a0
move.w (a0)+,(a0)
subq #4,a6 ; Make room for double result cell
move.l a6,d0 ; Save result cell addr
push.l a0 ; Source (the float data)
push.l d0 ; Dest (the result cell)
push.w #$3010 ; Extended to Comp
exg a6,a7
call pack4
exg a6,a7
;code
:code >FLOAT \ ( int32 -- flt )
push.l a6 ; Push ptr to the longint
bsr dic[(fltNew)] ; New float to A0
move.l a0,d2 ; Save in D2
addq.l #2,d2
push.l d2
push.w #$280E ; Longint to Extended
exg a6,a7
call pack4
exg a6,a7
move.l d2,a0
move.w (a0),-(a0)
clr.w 2(a0)
move.l a0,(a6) ; Replace the long cell with
; float ptr
;code
:code D>FLOAT \ ( int64 -- flt )
push.l a6 ; Push ptr to the longint
bsr dic[(fltNew)] ; New float to A0
move.l a0,d2 ; Save in D2
addq.l #2,d2
push.l d2
push.w #$300E ; Comp to Extended
exg a6,a7
call pack4
exg a6,a7
addq #4,a6
move.l d2,a0
move.w (a0),-(a0)
clr.w 2(a0)
move.l a0,(a6) ; Replace the double cell with
; float ptr
;code
\ ============= Environmental control =============
0 value TMP
:code GETENV \ ( -- env )
exg a6,a7
pea 2(dic[tmp])
move.w #3,-(a7) ; FGETENV
call pack4
exg a6,a7
moveq #0,d0
move.w 2(dic[tmp]),d0
push.l d0
;code
:code SETENV \ ( env -- )
pop.l dic[tmp]
exg a6,a7
pea 2(dic[tmp])
move.w #1,-(a7) ; FSETENV
call pack4
exg a6,a7
;code
\ =========== Masks for environment word ===========
hex
\ Rounding
2000 constant RoundUp
4000 constant RoundDown
6000 constant RoundToZero
\ Exception flags
0100 constant Invalid
0200 constant Underflow
0400 constant Overflow
0800 constant Zdivide
1000 constant Inexact
\ Halts
0001 constant InvHalt
0002 constant UfHalt
0004 constant OvHalt
0008 constant ZDHalt
0010 constant InxHalt
decimal
: SETHALT \ ( proc-addr -- )
-> tmp ['] tmp w 5 call pack4 ;
: GETHALT \ ( -- proc-addr )
['] tmp w 7 call pack4 tmp ;
:proc FPERR ." FP error" cr
i->l ." opcode " .h cr
." dst addr " .h cr
." src addr " .h cr
." src2 addr " .h cr
." misc rec ptr " .h cr ;proc
' FPerr sethalt
\ ===================================
\ FP named parms and locals
\ ===================================
\ In Mops, parms/locals are in D4-D7, and in the ExtraLocals area.
\ Any floating locals have the float ptr in the D reg or XL location.
\ To fetch a floating local, we compile
\
\ move.l <whatever>,A1
\ jsr Lfloat
\
\ and to store or whatever to a floating local, we compile
\
\ move.l <whatever>,D2
\ move.w #<opcode>,D1
\ jsr ToLfloat
\ move.l D2,<whatever>
\
\ Handlers does the hard work of generating this code (which isn't very
\ hard, really). Lfloat and ToLfloat are forward defined in the nucleus,
\ and are resolved here.
\ Note also, that for F@ which we use for some floating array accesses,
\ we JSR to Lfloat+8, thus skipping the check for the status word that
\ precedes scalar floats.
init: fltMem \ In case we're reloading
:code FPOPS
fadd.x (a0),fp0
rts
nop
fsub.x (a0),fp0
rts
nop
fmul.x (a0),fp0
rts
nop
fdiv.x (a0),fp0
;code
:code (LFLOAT)
loc
cmpi.w #1,-2(a1) ; Check source
bne.s noflt
; F@ comes in here.
bsr dic[(fltNew)] ; Get new float to A0
push.l a0 ; Push as result
movit movem.l (a1),d0-d2 ; Move data
movem.l d0-d2,(a0)
rts
noflt move.l a1,a0
bra dic[NoFloat]
;code
:code (TOLFLOAT)
tst.l d1
bpl.s operate
tst.l d2
beq.s noDisp
move.l d2,a0
bsr dic[(fltDisp)]
noDisp pop.l d2
rts
operate tst.l d2
beq dic[notInit]
oprt1 cmpi.w #$003E,d1
bhs.s AbsNeg
tst.b 3(dic[FPU?])
beq.s noFPU
move.l d2,a0
fmove.x (a0),fp0
move.l (a6)+,a0
lea dic[FPops],a1
lsl.w #2,d1
jsr 0(a1,d1.w)
bsr dic[(fltDisp)] ; Do Fxxx (A0),FP0
move.l d2,a0
fmove.x fp0,(a0)
rts
AbsNeg move.l d2,a0 ; Doesn't change CC
bhi.s Neg
bclr #7,(a0)
rts
Neg bchg #7,(a0)
rts
noFPU move.l (a6),a0
bsr dic[(fltDisp)]
move.l (a6),a0
move.w (a0)+,(a0)
move.l a0,(a6)
move.l d2,a0
move.w (a0)+,(a0)
push.l a0
push.w d1
exg a6,a7
call pack4
exg a6,a7
move.l d2,a0
move.w 2(a0),(a0)
clr.w 2(a0)
;code
:code (TOFVAL)
move.l a1,d2
tst.w d1
bpl oprt1
pop.l a0
movem.l (a0),d0-d2
movem.l d0-d2,(a1)
bra dic[(fltDisp)]
;code
\ (LFDISP) disposes of floating locals and parms at the end of a definition.
\ D2 = FltFlg, modified and shifted to exclude any operands in FP regs, so
\ that the rightmost bit always means D4, and so on. This longword has a
\ bit set for every operand we need to dispose.
:code (LFDISP)
loc
lsr.l #1,d2
bcc.s chkd5a
tst.l d4
beq.s chkd5
move.l d4,a0
bsr dic[(fltDisp)]
chkd5 tst.l d2
chkd5a beq.s end
lsr.l #1,d2
bcc.s chkd6a
tst.l d5
beq.s chkd6
move.l d5,a0
bsr dic[(fltDisp)]
chkd6 tst.l d2
chkd6a beq.s end
lsr.l #1,d2
bcc.s chkd7a
tst.l d6
beq.s chkd7
move.l d6,a0
bsr dic[(fltDisp)]
chkd7 tst.l d2
chkd7a beq.s end
lsr.l #1,d2
bcc.s chkXLa
tst.l d7
beq.s chkXL
move.l d7,a0
bsr dic[(fltDisp)]
chkXL tst.l d2
chkXLa beq.s end
lea dic[ExtraLocals],a1
XLloop lsr.l #1,d2
bcc.s XLnxta
tst.l (a1)
beq.s XLnxt
move.l (a1),a0
bsr dic[(fltDisp)]
XLnxt tst.l d2
XLnxta beq.s end
addq.l #4,a1
bra.s XLloop
end
;code
\ ====================================
\ Fvalues and Fcons
\ ====================================
\ In Mops, we handle Fvalues and Fcons along the same lines as floating
\ locals (which is logical). Thus, to fetch an Fvalue/Fconstant, we compile
\
\ lea <addr>,a1
\ jsr Lfloat
\
\ and to store or whatever to a floating Value, we compile
\
\ lea <addr>,a1
\ move.w #<opcode>,d1
\ jsr ToFval
\
\ As usual, Handlers takes care of this for us. Here, we just have to make
\ sure that Fvalues and Fcons get the right handler code. We also put a
\ "1" word in front of the float, so that Lfloat and ToLfloat won't think
\ it's an error. They handle floating named parameters as well, so they do
\ need to check.
\ An FCRcon is essentially an Fcon, but is used for constants that are in
\ the 68881/2 ROM. If we're compiling FPU code we use the ROM version which
\ is a lot faster. But the floating value is stored in the dic as for an
\ Fcon as well in case there's no FPU.
: FLIT, \ ( flt -- )
\ Writes a float into dictionary: analogous to , or c,
\ We omit the 2 unused bytes. If we're compiling FPU code,
\ we call CompFPUL instead of coming here.
dup w@ here w! 2 allot
dup 4+ here 8 cmove 8 allot fdrop ;
: FCON, \ ( flt -- )
\ As for FLIT, but we include the 2 unused bytes. We handle
\ FCONs and FVALs this way, since they are operated on by the
\ same code as for floating locals.
dup here 12 cmove 12 allot fdrop ;
: FVALUE
header
FvalCode w, \ Handler code
1 w, fcon, ;
: FCON
header
-76 w, \ Fcon handler code
1 w, fcon, ;
: FCRCON \ ( offs -- )
header
-88 w, \ FCRcon handler code
w, \ ROM offset
1 w, fcon, ;
header F@
-100 w, \ handler code
xinfoMk w, \ "extra non-code info" of zero length means
0 w, \ compilation only
header F!
-102 w, \ handler code
xinfoMk w, \ "extra non-code info" of zero length means
0 w, \ compilation only
\ =====================================
\ FP to/from decimal conversion
\ =====================================
\ Some useful constants:
256 constant NEG
0 constant POS
256 constant FixedDecimal
0 constant FloatDecimal
false value VALID? \ Needed by the scanner. But we never
\ use it otherwise.
:code FP> \ ( flt -- flt )
move.l (a6),a0
cmpi.w #1,-2(a0)
bne dic[noFloat]
move.w (a0)+,(a0)
;code
:class DEC super{ object }
\ SANE Record Decimal ( x = (-1)^sign * 10^exp * digits )
int SIGN
int EXP
22 bytes DIGITS \ to fake string[20] ; 22 to make even
int INDEX \ Used by the scanner.
\ SANE Record DecForm
int STYLE
int #DIGITS \ # of sig digits,if float;
\ # dec. places,if fixed.
:m CLEAR:
addr: sign 26 erase ;m
:m EINIT: clear: self FloatDecimal put: style 19 put: #digits ;m
:m FINIT: clear: self FixedDecimal put: style ;m
:m SETSTYLE: put: style ;m
:m SET#DIGITS: put: #digits ;m
:m SETEXP: put: exp ;m
:m EXP: get: exp ;m
:m SIGN: get: sign ;m
:m ZERO: \ Puts a zero in decimal record
clear: self $ 0130 addr: #digits w! ;m
:m >FLOAT: { \ flt -- flt }
^base \ Addr of decimal record
new: fltMem -> flt flt 2+ \ Destination address
$ 0009 \ FFEXT FOD2B + -- Opcode for decimal to
\ binary; dest=extended
fp68k flt adjust_result
;m
\ =>: converts the passed-in float to decimal.
:m =>: { flt -- }
addr: style \ Addr of decform record
flt FP> 2+ \ Addr of source
^base \ Addr of decimal record
$ 000B \ FFEXT FOB2D + -- Opcode for binary to
\ decimal; source=extended
fp68k flt fdrop ;m \ Call SANE, dispose of float
\ Ascii input
:m SCAN: \ ( addr len -- )
str255 1+
clear: index addr: index
^base ['] valid? 3+ w 2 call Pack7 ;m
:m CONV?: { addr len -- b }
\ Attempts to convert the passed-in string, using SCAN:.
\ Returns True if all the input was read. Otherwise
\ we assume the terminating (non-scanned) character is
\ invalid, and return False.
addr len scan: self
get: index len = ;m
\ Ascii output
:m FORMAT: \ ( -- addr len )
addr: style ^base pad w 3 call Pack7
pad count ;m
:m PRINT:
format: self type ;m
:m DUMP:
." sign: " get: sign IF & - ELSE & + THEN emit cr
." exp: " get: exp . cr
addr: digits count type cr
." style: " get: style IF ." fixed" ELSE ." float" THEN cr
." index: " get: index . cr
." #digits: " get: #digits . cr ;m
;class
dec theDec
: #DIGITS set#digits: theDec ;
: E.R { flt wid \ svOut -- }
out -> svOut
floatDecimal setStyle: theDec
wid 6 - #digits \ Allow for point, sign, and e+nn
flt =>: theDec
print: theDec
wid out svOut - - spaces ;
: E. 26 e.r ;
: F.R { flt wid \ #dig svOut -- }
out -> svOut
floatDecimal setStyle: theDec
wid 2- #digits \ Allow for sign and dec point
flt =>: theDec
fixedDecimal setStyle: theDec
exp: theDec negate dup -> #dig #digits
sign: theDec NIF space THEN
#dig NIF space THEN \ In this case, no dec point
print: theDec
wid out svOut - - spaces ;
: FCONV? { addr len \ flt -- flt T | -- F }
\ Converts the passed-in ASCII string to
\ floating, if possible. I like this name better
\ than ATOF which Neon had, but change it back if
\ you want to.
addr len conv?: thedec NIF false EXIT THEN
new: fltMem -> flt
thedec flt 2+ 9 FP68K
flt adjust_result true ;
\ ==============================
\ Interpretation
\ ==============================
: FNUMBER \ ( addr -- flt T | -- F )
\ Attempts to convert token at addr to a float.
count fconv? ;
: FLITERAL { flt -- } \ Compiles an in-line float.
useFPU?
IF flt 8 + @ flt 4+ @ flt @ compFPUL flt fdrop
ELSE postpone flit flt flit,
THEN ;
: (FNUM) { addr -- flt T | -- addr F }
\ Checks if string at Here is a float, defined by containing
\ a decimal point. Error if there is a point, but not a legal
\ float.
addr count & . scan nip NIF addr false EXIT THEN
addr fnumber ?notFound
state IF fLiteral THEN true ;
: FLOAT? { adr -- b }
adr 1 and IF false EXIT THEN
adr ['] fltmem >
adr ['] (fltnew) <
and NIF false EXIT THEN
adr 2- w@ 1 = ;
' (Lfloat) -> ^Lfloat \ So f@ below will compile properly
: (.CELLF) { adr -- } \ FP version of stack cell typing word
adr @ float?
IF adr @ f@ e.
ELSE adr @ .
THEN ;
: FPINIT \ Initialization word for FP package
init: fltMem
['] (fltNew) -> ^FPnew
['] (fltDisp) -> ^FPdisp
['] (fltDisp2) -> ^FPdisp2
['] (Lfloat) -> ^Lfloat
['] (ToLfloat) -> ^ToLfloat
['] (ToFval) -> ^ToFval
['] (LFdisp) -> ^LFdisp
['] (FPUlit) -> ^FPULit
['] (.cellf) -> .cell
;
: CLEANFLOAT \ New error word
cl3 init: fltMem ;
: MOPS>FLT
['] (Fnum) -> Fnum?
['] FPinit add: init_actions
['] cleanFloat -> abortVec ;
: MOPS>INT
0 -> Fnum?
['] FPinit removeXT: init_actions
['] cl3 -> abortVec ;
FPinit mops>flt
\ =================================
\ Transcendentals
\ =================================
:code LN \ Natural log
move.l (a6),a0
move.w (a0)+,(a0)
push.l a0
exg a6,a7
clr.w -(a7) ; FLNX code
call pack5
exg a6,a7
bra dic[adjust_result]
;code
:code LOG2 \ Base 2 log
move.l (a6),a0
move.w (a0)+,(a0)
push.l a0
exg a6,a7
move.w #2,-(a7) ; FLOG2X
call pack5
exg a6,a7
bra dic[adjust_result]
;code
:code LN1 \ ln(1+x)
move.l (a6),a0
move.w (a0)+,(a0)
push.l a0
exg a6,a7
move.w #4,-(a7) ; FLN1X
call pack5
exg a6,a7
bra dic[adjust_result]
;code
:code LOG21 \ log2(1+x). I don't think LOG21 is a very helpful name
\ (pure computerese), but I guess we're stuck with it.
move.l (a6),a0
move.w (a0)+,(a0)
push.l a0
exg a6,a7
move.w #6,-(a7) ; FLOG21X
call pack5
exg a6,a7
bra dic[adjust_result]
;code
:code EXP \ Base e exponential
move.l (a6),a0
move.w (a0)+,(a0)
push.l a0
exg a6,a7
move.w #8,-(a7) ; FEXPX
call pack5
exg a6,a7
bra dic[adjust_result]
;code
:code EXP2 \ Base 2 exponential
move.l (a6),a0
move.w (a0)+,(a0)
push.l a0
exg a6,a7
move.w #$A,-(a7) ; FEXP2X
call pack5
exg a6,a7
bra dic[adjust_result]
;code
:code EXP1 \ e**x - 1
move.l (a6),a0
move.w (a0)+,(a0)
push.l a0
exg a6,a7
move.w #$C,-(a7) ; FEXP1X
call pack5
exg a6,a7
bra dic[adjust_result]
;code
:code EXP21 \ 2**x - 1
move.l (a6),a0
move.w (a0)+,(a0)
push.l a0
exg a6,a7
move.w #$E,-(a7) ; FEXP21X
call pack5
exg a6,a7
bra dic[adjust_result]
;code
:code **N \ ( x n -- x**n ) Integer exponentiation. This wasn't
\ in Neon, but might be useful. Note this operation
\ ignores the high-order 16 bits of n.
loc
pop.l hi
move.l (a6),a0
move.w (a0)+,(a0)
exg a6,a7
pea lo
move.l a0,-(a7)
move.w #$8010,-(a7) ; FXPWRI
call pack5
exg a6,a7
bra dic[adjust_result]
hi dc.w 0
lo dc.w 0
;code
:code F** \ ( x y -- x**y ) General exponentiation - takes 2 floats.
\ Here I think the Neon name was crazy. But we've still
\ got it below for compatibility.
bsr dic[fop2]
move.w #$8012,-(a6) ; FXPWRY
exg a6,a7
call pack5
exg a6,a7
bra dic[adjust_result]
;code
:code X**Y \ For Neon compatibility
bra.s dic[f**]
;code
\ Financial functions. I never have enough finances to need these myself.
:code COMPOUND \ ( rate #periods -- compound_interest)
bsr dic[(fltNew)] ; New float to A0 (will be
; result). Must get before
move.l a0,d2 ; killing src floats. Save in D2
pop.l a0
pop.l a1
move.w (a0)+,(a0)
move.w (a1)+,(a1)
push.l a1
push.l a0
subq #2,a0
subq #2,a1
bsr dic[(fltDisp2)] ; Kill source floats
move.l d2,a0
move.w (a0)+,(a0)
push.l a0 ; Destination
push.w #$C014
exg a6,a7
call pack5
exg a6,a7
push.l d2
bsr dic[adjust_result]
;code
:code ANNUITY \ ( rate #periods -- annuity)
bsr dic[(fltNew)] ; New float to A0 (will be
; result). Must get before
move.l a0,d2 ; killing src floats. Save in D2
pop.l a0
pop.l a1
move.w (a0)+,(a0)
move.w (a1)+,(a1)
push.l a1
push.l a0
subq #2,a0
subq #2,a1
bsr dic[(fltDisp2)] ; Kill source floats
move.l d2,a0
move.w (a0)+,(a0)
push.l a0 ; Destination
push.w #$C016
exg a6,a7
call pack5
exg a6,a7
push.l d2
bsr dic[adjust_result]
;code
\ Trig functions.
$ 56 :fp1 SIN
ToCode
loc
tst.b 3(dic[FPU?])
beq.s noFPU
move.l (a6),a0
fsin.x (a0),fp0
fmove.x fp0,(a0)
rts
noFPU move.l (a6),a0
move.w (a0)+,(a0)
push.l a0
exg a6,a7
move.w #$18,-(a7) ; FSINX
call pack5
exg a6,a7
bra dic[adjust_result]
;code
$ 57 :fp1 COS
ToCode
loc
tst.b 3(dic[FPU?])
beq.s noFPU
move.l (a6),a0
fcos.x (a0),fp0
fmove.x fp0,(a0)
rts
noFPU move.l (a6),a0
move.w (a0)+,(a0)
push.l a0
exg a6,a7
move.w #$1A,-(a7) ; FCOSX
call pack5
exg a6,a7
bra dic[adjust_result]
;code
$ 58 :fp1 TAN
ToCode
loc
tst.b 3(dic[FPU?])
beq.s noFPU
move.l (a6),a0
ftan.x (a0),fp0
fmove.x fp0,(a0)
rts
noFPU move.l (a6),a0
move.w (a0)+,(a0)
push.l a0
exg a6,a7
move.w #$1C,-(a7) ; FTANX
call pack5
exg a6,a7
bra dic[adjust_result]
;code
$ 59 :fp1 ARCTAN
ToCode
loc
tst.b 3(dic[FPU?])
beq.s noFPU
move.l (a6),a0
fatan.x (a0),fp0
fmove.x fp0,(a0)
rts
noFPU move.l (a6),a0
move.w (a0)+,(a0)
push.l a0
exg a6,a7
move.w #$1E,-(a7) ; FATANX
call pack5
exg a6,a7
bra dic[adjust_result]
;code
:code FRAND \ floating-pt random number routine
move.l (a6),a0
move.w (a0)+,(a0)
push.l a0
exg a6,a7
move.w #$20,-(a7)
call pack5
exg a6,a7
bra dic[adjust_result]
;code
\ ======================================
\ Sundry useful constants and operations
\ ======================================
1.0 fcon 1.0
0.0 fcon 0.0
1.0 exp fcon E
10.0 ln fcon LN(10)
0.0 fcon PI \ Not the real value yet!!!
0.0 fcon UNDEF \ Ditto
: SetPi { \ adr -- } \ Sets up PI according to the value in the
\ 68882 ROM.
['] pi -> adr
$ 40000000 adr 2+ !
$ C90FDAA2 adr 6 + !
$ 2168C235 adr 10 + ! ;
: SetUndef { \ adr -- } \ Sets up UNDEF to NAN(255).
['] undef -> adr
$ 7FFF0000 adr 2+ !
$ FFFFFFFF adr 6 + !
$ FFFFFFFF adr 10 + ! ;
SetPi SetUndef forget SetPi
\ 0 FCRcon PI
: 1/X 1.0 swap f/ ;
: LOG \ ( x -- log(x) ) Log base 10 of x
ln ln(10) f/ ;
: ANTILOG \ ( x -- antilog(x) ) Antilog ( 10^x ) of x
ln(10) f* exp ;
: COT \ ( x -- cot(x) ) Cotangent of x
tan 1/x ;
: DEG2RAD \ ( deg -- rad ) Converts degrees to radians
pi f* 180. f/ ;
: RAD2DEG \ ( rad -- deg ) Converts radians to degrees
180. f* PI f/ ;
\ ===================================
\ Class Float
\ ===================================
\ Class Float allows a floating value to be a high-level object, which
\ means it can be an ivar. There is something of a performance
\ penalty if FPU code is being generated, since a Float object must
\ be in main memory, which increases the amount of data movement
\ between the FPU and the integer unit. This is slow on a 68030, but
\ shouldn't be such a problem on a 68040.
:class FLOAT super{ object }
12 bytes data
:m GET: \ ( -- x ) Pushes private data onto stack
inline{ obj f@} ^base f@ ;m
:m PUT: \ ( x -- ) store float into private data
inline{ obj f!} ^base f! ;m
:m ->: \ ( float -- ) Assigns value of passed-in Float to this Float
inline{ f@ obj f!}
f@ ^base f! ;m
\ ----- Arithmetic operations take a stack float (not a Float obj)
:m +:
inline{ obj f@ f+ obj f!}
^base f@ f+ ^base f! ;m
:m -:
inline{ obj f@ f- obj f!}
^base f@ f- ^base f! ;m
:m *:
inline{ obj f@ f* obj f!}
^base f@ f* ^base f! ;m
:m /:
inline{ obj f@ f/ obj f!}
^base f@ f/ ^base f! ;m
:m SIN: \ ( -- sin ) returns sine of object
inline{ obj f@ sin}
^base f@ sin ;m
:m COS: \ ( -- cos ) returns cosine of object
inline{ obj f@ cos}
^base f@ cos ;m
:m TAN: \ ( -- tan ) returns tangent of object
inline{ obj f@ tan}
^base f@ tan ;m
:m ARCTAN: \ ( -- arcTan) returns arctangent of object
inline{ obj f@ arctan}
^base f@ arctan ;m
:m LN: \ ( -- ln) returns natural log of object
inline{ obj f@ ln}
^base f@ ln ;m
:m EXP: \ ( -- exp ) returns exp of object
inline{ obj f@ exp}
^base f@ exp ;m
:m LOG: \ ( -- log ) returns log base 10 of object
inline{ obj f@ log}
^base f@ log ;m
:m ANTILOG: \ ( -- 10**x ) returns antilog of object
inline{ obj f@ antilog}
^base f@ antilog ;m
:m DEG: \ ( -- degrees ) converts radians to degrees
inline{ obj f@ rad2deg}
^base f@ rad2deg ;m
:m RAD: \ ( -- radians ) converts from radians to degrees
inline{ obj f@ deg2rad}
^base f@ deg2rad ;m
:m ABSVAL: \ ( -- abs ) Returns absolute value.
inline{ obj f@ fabs}
^base f@ fabs ;m
:mcode ABS: \ ( -- ) Replaces obj's data with its absolute. Doesn't
\ return anything.
bclr #7,(a2)
;mcode
:m NEG: \ ( -- val ) Returns object value with sign negated
inline{ obj f@ fnegate}
^base f@ fnegate ;m
:mcode NEGATE: \ ( -- ) Negates the object's data. Doesn't return anything.
bchg #7,(a2)
;mcode
:m PRINT: ^base f@ e. ;m
;class
\ =================================
\ Floating arrays
\ =================================
:code (^ELEM) \ ( idx -- ) A subroutine to get the element addr to A1.
loc
pop.l d0 ; d0 = index
move.l a2,a1
add.w -2(a1),a1 ; now a1 -> ^class
add.w -2(a1),a1 ; now a1 -> start of indexed area
tst.w -4(a1) ; Skip bounds check if this is
bne.s mul12 ; a LARGE farray
chk -2(a1),d0 ; bounds check
mul12 move.l d0,d1 ; mult by 12 and add to index base in a1
add.l d1,d0
add.l d1,d0
asl.l #2,d0
add.l d0,a1 ; Element addr to a1
;code
:class FARRAY super{ indexed-obj } 12 indexed
:mcode ^ELEM: \ ( idx -- addr )
bsr.s dic[(^Elem)]
push.l a1
;mcode
:mcode AT:
bsr dic[(^Elem)] ; Get element addr to a1
bsr dic[(fltNew)] ; New float to a0
push.l a0 ; Push it
movem.l (a1),d0-d2
movem.l d0-d2,(a0) ; Move data over
;mcode
:mcode TO: \ ( flt idx -- )
bsr dic[(^Elem)] ; Get element addr to a1
pop.l a0
movem.l (a0),d0-d2 ; Move data over
movem.l d0-d2,(a1)
bsr dic[(fltDisp)] ; Dispose of stack float
;mcode
:m FILL: \ ( x -- ) Fills all elements with x
limit 0 DO fdup i to: self LOOP fdrop ;m
:m PRINT: \ Prints all elements
limit: self 0 ?DO i dup 4 .r space at: self e. cr
LOOP ;m
:m CLASSINIT:
undef
limit: self FOR fdup i to: self NEXT fdrop ;m
;class